﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;
namespace Tressel_asm
{
    public partial class CodeDisplayForm : Form
    {
        [STAThread]
        static void Main()
        {
            /*!
            Procedure Name: Main
            Description: The main entry point of the application
            Input Parameters: none
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 10/12/2009
            Modification Log:
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new CodeDisplayForm());
        }

        public CodeDisplayForm()
        {
            /*!
            Procedure Name: DesignForm
            Description: The constructor for the form
            Input Parameters: none
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 10/12/2009
            Modification Log:
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            InitializeComponent();
            displayTimer = new Timer();
            displayTimer.Interval = 200;
            displayTimer.Tick += new EventHandler(DisplayTimerTick);
            warningECellStyle = new DataGridViewCellStyle();
            warningECellStyle.ForeColor = System.Drawing.Color.Blue;
            this.intermediateECellStyle = new DataGridViewCellStyle();
            intermediateECellStyle.ForeColor = System.Drawing.Color.Orange;
            fatalECellStyle = new DataGridViewCellStyle();
            fatalECellStyle.ForeColor = System.Drawing.Color.Red;
        }

        void DisplayTimerTick(object sender, EventArgs e)
        {
            /*!
            Procedure Name: DisplayFormTick
            Description: called when the displayTimer ticks, triggers the parsing of the program
            Input Parameters: object sender, EventArgs e
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 11/2/2009
            Modification Log:
            Who: Steve Rudy
            When: 11/7/2009
            Why: with the DisplaySourceAndErrors method created, we needed to call it here
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            /*#
            Variable Name: isError
            Module Where Defined: DesignForm
            Data Type: Button
            Local or Global: Local
            Purpose: used to determine whether an error is present
            #*/
            displayTimer.Stop();
            tresselSourceDataGridView.Rows.Clear();
            intermediateVersionDataGridView.Rows.Clear();
            bool isError = false;
            try
            {
                pass1 = TresselIntermediate.Parse(fileLocationTextBox.Text, this);
            }
            catch (System.Data.OleDb.OleDbException)
            {
                isError = true;
                MessageBox.Show("Unable to find tables.  Are they in the directory of the built application?");
            }
            if (!isError)
            {
                this.symbolTableButton.Enabled = true;
                this.tresselSourceLabel.Enabled = true;
                this.intermediateVersionLabel.Enabled = true;
                DisplayInstructions(pass1.IntermediateInstructions);
                if (pass1.FatalErrors)
                {
                    DisplaySourceAndErrors();
                    MessageBox.Show("Fatal errors exist in first pass.  Second pass cannot execute.");
                }
                else
                {
                    RunPass2();
                }
            }
        }
        /*#
        Variable Name: displayTimer
        Module Where Defined: CodeDisplayForm
        Data Type: Timer
        Local or Global: Global
        Purpose:  used to control when to parse the program after the dialog closes
        #*/
        private Timer displayTimer;
        public void RunPass2()
        {
            /*!
            Procedure Name: RunPass2
            Description: runs pass2
            Input Parameters: none
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 11/2/2009
            Modification Log:
            Who: Shawn
            When: 11/3/2009
            Why: Changed the newlines in the file output so that they worked in all text editors
            Who: Steve Rudy
            When: 11/10/2009
            Why: added code to create the assembly listing and sorted symbol file
            Who: Steve Rudy
            When: 11/12/2009
            Why: added the fileName string and renamed all output files
            Who: Steve Rudy
            When: 11/12/2009
            Why: made assembly and object file names the same as the original source, and created new directories for these files
            Who: Steve Rudy
            When: 11/13/2009
            Why: converted the '\n' character to Environment.NewLine to make the program more flexible
            Who: Steve Rudy
            When: 11/13/2009
            Why: needed to copy the input file to the input directory
            Who: Steve Rudy
            When: 11/15/2009
            Why: the messageboxes needed to be placed after pass2 is called
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            /*#
            Variable Name: fileOutput
            Module Where Defined: RunPass2
            Data Type: string
            Local or Global: Local
            Purpose: holds the object file data
            #*/
            string fileOutput = "";
            pass2 = new ObjectFile(pass1);
            /*#
            Variable Name: shouldContinue
            Module Where Defined: RunPass2
            Data Type: bool
            Local or Global: Local
            Purpose: tracks if we should make object file and assembly listing
            #*/
            bool shouldContinue = true;
            DisplaySourceAndErrors();
            if (pass1.GeneratedErrorTable.Rows.Count > 0)
            {
                if (MessageBox.Show("Non-fatal errors found.  Generate object file and assembly listing?", "", MessageBoxButtons.YesNo).Equals(DialogResult.No))
                {
                    shouldContinue = false;
                }
            }
            if (shouldContinue)
            {
                for (int i = 0; i < pass2.ObjectFileArray.Count; i++)
                {
                    if (i < pass2.ObjectFileArray.Count - 1)
                    {
                        fileOutput += pass2.ObjectFileArray[i].ToString() + System.Environment.NewLine;
                    }
                    else
                    {
                        fileOutput += pass2.ObjectFileArray[i].ToString();
                    }
                }
                /*#
                Variable Name: fileName
                Module Where Defined: CodeDisplayForm
                Data Type: string
                Local or Global: Local
                Purpose: used to hold the filename that all output files will have
                #*/
                string fileName = openFileDialog1.FileName.Substring(openFileDialog1.FileName.LastIndexOf("\\") + 1);
                if (!fileName.EndsWith(".txt"))
                {
                    fileName = fileName + ".txt";
                }
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\object");
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\assembly");
                DirectoryInfo sd = Directory.CreateDirectory(System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\input");
                System.IO.TextWriter tw = new StreamWriter(System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\object\\" + fileName, false, Encoding.ASCII);
                tw.Write(fileOutput);
                tw.Flush();
                tw.Close();
                tw = new StreamWriter(System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\assembly\\" + fileName, false, Encoding.ASCII);
                /*#
                Variable Name: assemblyOutput
                Module Where Defined: RunPass2
                Data Type: string
                Local or Global: Local
                Purpose: holds the assembly listing
                #*/
                string assemblyOutput = "";
                for (int i = 0; i < pass2.AssemblingListingArray.Count; i++)
                {
                    if (i < pass2.AssemblingListingArray.Count - 1)
                    {
                        assemblyOutput += pass2.AssemblingListingArray[i].ToString() + System.Environment.NewLine;
                    }
                    else
                    {
                        assemblyOutput += pass2.AssemblingListingArray[i].ToString();
                    }
                }
                assemblyOutput = assemblyOutput + Environment.NewLine + Environment.NewLine + Environment.NewLine + "Sorted Symbol Table" + Environment.NewLine + Environment.NewLine + "Label\tAddress\tSubstitution\tUsage" + Environment.NewLine;
                /*#
                    Variable Name: storedRows
                    Module Where Defined: RunPass2
                    Data Type: DataRow[]
                    Local or Global: Local
                    Purpose: gets the rows from symbolTable.
                    #*/
                DataRow[] sortedRows = pass1.SymbolTable.Select("", "[Label Name] ASC");
                foreach (DataRow row in sortedRows)
                {
                    /*#
                    Variable Name: potentialTab
                    Module Where Defined: RunPass2
                    Data Type: string
                    Local or Global: Local
                    Purpose: helps line up columns when symbol table is outputted
                    #*/
                    string potentialTab = " ";
                    /*#
                    Variable Name: address
                    Module Where Defined: RunPass2
                    Data Type: string
                    Local or Global: Local
                    Purpose: holds the address so it can be formatted into hex
                    #*/
                    string address = row["Address"].ToString();
                    if (address.Length > 0)
                    {
                        address = TresselIntermediate.ToHex(Convert.ToInt32(address));
                    }
                    if (row["Label Name"].ToString().Length < 8)
                    {
                        potentialTab = "\t";
                    }
                    assemblyOutput = assemblyOutput + row["Label Name"].ToString() + potentialTab + address + "\t" + row["Substitution String"].ToString() + "\t\t" + row["Usage"].ToString() + Environment.NewLine;
                }
                tw.Write(assemblyOutput);
                tw.Flush();
                tw.Close();
                if (!File.Exists(System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\input\\" + fileName))
                {
                    File.Copy(fileLocationTextBox.Text, System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\input\\" + fileName, true);
                }
                System.Diagnostics.Process.Start(System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\object\\" + fileName);
                System.Diagnostics.Process.Start(System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\assembly\\" + fileName);
            }
        }
        private void DesignFormLoad(object sender, EventArgs e)
        {
            /*!
            Procedure Name: DesignFormLoad
            Description: fires when the form loads
            Input Parameters: object sender, EventArgs e
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 10/12/2009
            Modification Log:
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            this.openFileDialog1.InitialDirectory = System.Environment.CurrentDirectory;
        }

        private void OpenFileDialog1FileOk(object sender, CancelEventArgs e)
        {
            /*!
            Procedure Name: OpenFileDialog1FileOk
            Description: fires when the tressel source file is selected; puts the path into the textbox and assembles the file
            Input Parameters: object sender, EventArgs e
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 10/12/2009
            Modification Log:
            Who: Steve Rudy
            When: 11/2/2009
            Why: adding the preparation code for pass2 to be executed
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            this.fileLocationTextBox.Text = openFileDialog1.FileName;
            displayTimer.Start();
            
        }
        private void DisplayInstructions(System.Collections.ArrayList instructionsArray)
        {
            /*!
            Procedure Name: DisplayInstructions
            Description: shows the intermediate code
            Input Parameters: System.Collections.ArrayList instructionsArray
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 10/17/2009
            Modification Log:
            Who: Steve Rudy
            When: 11/7/2009
            Why: Method should be private, as it won't be used outside the CodeDisplayForm
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            for (int i = 0; i < instructionsArray.Count; i++)
            {
                /*#
                Variable Name: tempInstruction
                Module Where Defined: DisplayInstructions
                Data Type: Instruction
                Local or Global: Local
                Purpose: holds each instruction as it is retrieved from the array
                #*/
                Instruction tempInstruction = (Instruction)instructionsArray[i];
                /*#
                Variable Name: newRow
                Module Where Defined: DisplayInstructions
                Data Type: string[]
                Local or Global: Local
                Purpose: holds the data to be inserted into the intermediateVersionDataGridView
                #*/
                string[] newRow = { tempInstruction.ILineCount.ToString(), tempInstruction.ILocationCount.ToString(),tempInstruction.ILabel.Text, tempInstruction.IOpcode.Text, tempInstruction.IOperands.Text };
                this.intermediateVersionDataGridView.Rows.Add(newRow);
            }
        }
        private void BrowseButtonClick(object sender, EventArgs e)
        {
            /*!
            Procedure Name: BrowseButtonClick
            Description: fires when the browse button is clicked; opens the file location dialog
            Input Parameters: object sender, EventArgs e
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 10/12/2009
            Modification Log:
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            openFileDialog1.Reset();
            openFileDialog1.Title = "Select Tressel Source";
            this.openFileDialog1.ShowDialog();
        }
        private void SymbolTableButtonClick(object sender, EventArgs e)
        {
            /*!
            Procedure Name: SymbolTableButtonClick
            Description: shows the symbol table to the user
            Input Parameters: object sender, EventArgs e
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 10/18/2009
            Modification Log:
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/

            /*#
            Variable Name: symbolTableDisplay
            Module Where Defined: SymbolTableButtonClick
            Data Type: SymbolTableForm
            Local or Global: Local
            Purpose: displays the symbol table
            #*/
            SymbolTableForm symbolTableDisplay = new SymbolTableForm(this.pass1.SymbolTable);
            symbolTableDisplay.ShowDialog();
        }

        private void IntermediateVersionDataGridViewSelectionChanged(object sender, EventArgs e)
        {
            /*!
            Procedure Name: IntermediateVersionDataGridViewSelectionChanged
            Description: Blocks the user from selecting the intermediate code
            Input Parameters: object sender, EventArgs e
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 10/18/2009
            Modification Log:
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/

            /*#
            Variable Name: dgv
            Module Where Defined: IntermediateVersionDataGridViewSelectionChanged
            Data Type: DataGridView
            Local or Global: Local
            Purpose: holds the datagridview that called this method
            #*/
            DataGridView dgv = (DataGridView)sender;
            foreach (DataGridViewRow row in dgv.SelectedRows)
            {
                row.Selected = false;
            }
        }
        private void DisplaySourceAndErrors()
        {
            /*!
            Procedure Name: DisplaySourceAndErrors
            Description: displays the original tressel source and its errors underneath
            Input Parameters: none
            Output Parameters: none
            Error Conditions Tested:
            Error Messages Generated:
            Original Author: Steve Rudy
            Procedure Creation Date: 11/7/2009
            Modification Log:
            Who: Steve Rudy
            When: 11/11/2009
            Why: used the source object instead of retrieving the original source from the files
            Who: Steve Rudy
            When: 11/14/2009
            Why: error reporting was still reporting duplicates
            Coding Standards Met: SR
            Testing Standards Met: RM
            !*/
            tresselSourceDataGridView.Rows.Clear();
            /*#
            Variable Name: isFatalError
            Module Where Defined: DisplaySourceAndErrors
            Data Type: bool
            Local or Global: Local
            Purpose: used to indicate that a fatal error has been found and we shouldn't continue displaying
            #*/
            bool isFatalError = false;
            for (int i = 0;!isFatalError && i < pass1.Source.Count; i++)
            {
                /*#
                Variable Name: sourceRow
                Module Where Defined: DisplaySourceAndErrors
                Data Type: string[]
                Local or Global: Local
                Purpose: used to hold the row data to be added to the datagridview
                #*/
                string[] sourceRow = { i.ToString(), pass1.Source[i].ToString().Replace("\t","     ") };
                tresselSourceDataGridView.Rows.Add(sourceRow);
                /*#
                Variable Name: returnRows
                Module Where Defined: DisplaySourceAndErrors
                Data Type: DataRow[]
                Local or Global: Local
                Purpose: used to hold the return row collection for the generated error table search
                #*/
                DataRow[] returnRows = pass1.GeneratedErrorTable.Select("[Line Number]='" + i.ToString() + "'","Description ASC");
                /*#
                Variable Name: lastDescription
                Module Where Defined: DisplaySourceAndErrors
                Data Type: string
                Local or Global: Local
                Purpose: used to remember what the last type of error was
                #*/
                string lastDescription = "This holds the value of the last error description";
                for (int j = 0; j < returnRows.Length; j++)
                {
                    /*#
                    Variable Name: dr
                    Module Where Defined: DisplaySourceAndErrors
                    Data Type: DataRow
                    Local or Global: Local
                    Purpose: holds the specific row of the rows collection
                    #*/
                    DataRow dr = returnRows[j];
                    string descriptionString = dr["Description"].ToString();
                    if (!dr["Description"].ToString().Equals(lastDescription))
                    {
                        /*#
                        Variable Name: errorRow
                        Module Where Defined: DisplaySourceAndErrors
                        Data Type: string[]
                        Local or Global: Local
                        Purpose: holds the row data to be inserted into the source datagridview
                        #*/
                        string[] errorRow = { " ", dr["Description"].ToString() };
                        this.tresselSourceDataGridView.Rows.Add(errorRow);
                        switch (dr["Type"].ToString().ToUpper())
                        {
                            case "WARNING":
                                {
                                    tresselSourceDataGridView.Rows[tresselSourceDataGridView.Rows.Count - 1].DefaultCellStyle = warningECellStyle;
                                    break;
                                }
                            case "INTERMEDIATE":
                                {
                                    tresselSourceDataGridView.Rows[tresselSourceDataGridView.Rows.Count - 1].DefaultCellStyle = this.intermediateECellStyle;
                                    break;
                                }
                            case "FATAL":
                                {
                                    tresselSourceDataGridView.Rows[tresselSourceDataGridView.Rows.Count - 1].DefaultCellStyle = this.fatalECellStyle;
                                    isFatalError = true;
                                    break;
                                }
                        }
                        lastDescription = dr["Description"].ToString();
                    }
                }
            }
        }

        private void intermediateVersionDataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (e.ColumnIndex == 1)
            {
                e.Value = TresselIntermediate.ToHex(System.Convert.ToInt16(e.Value));
            }
        }
    }
}
